﻿using System;
using System.Diagnostics;
using System.Text;
using System.IO;
using System.Windows.Forms;
using SlimDX;

namespace ExceptionLog
{

    public enum LoggingLevel
    {
        Low,
        Medium,
        High
    }

    /// <summary>
    /// Die Klasse schreibt die Fehlermeldungen in eine Datei.
    /// </summary>
    public class Logger
    {

        #region Fields

        private static LoggingLevel logLevel;
        private static string logFile;
        private static bool writeDateTimeEverytime;


        #endregion


        #region Properties

        /// <summary>
        /// Eigenschaft: Ruft den aktuellen Namen der Protokolldatei ab. (schreibgeschützt)
        /// </summary>
        public static string LogFile
        {
            get { return logFile; }
        }

        /// <summary>
        /// Eigenschaft: Ruft den aktuellen Namen der Protokolldatei ab. (schreibgeschützt)
        /// </summary>
        public static bool WriteDateTimeEverytime
        {
            get { return writeDateTimeEverytime; }
            set { writeDateTimeEverytime = value; }
        }

        /// <summary>
        /// Eigenschaft: Liest und setzt das Protokoll-Level
        /// </summary>
        public static LoggingLevel LogLevel
        {
            get { return logLevel; }
            set { logLevel = value; }
        }


        #endregion


        #region Constructors

        ///<summary>
        /// Standardkonstruktor: Er legt den Dateinamen nach den Standardkonvention der Klasse fest
        /// </summary>
        public Logger()
        {
            // Standardwert für Protokoll-Level festlegen
            logLevel = LoggingLevel.High;

            // Pfadname der Log-Datei wird mit einem StrinBuilder-Objekt zusammengebaut
            StringBuilder sb = new StringBuilder();

            // Pfad der ausgeführten Anwendung holen (ohne Dateinamen)
            sb.Append(Application.StartupPath);

            // Slash anhängen
            sb.Append("\\");

            // Datum im Kurzformat anhängen
            sb.Append(DateTime.Now.ToShortDateString());

            // Unterstrich anhängen
            sb.Append("_");

            // Namen der exe-Datei ermitteln und anhängen
            sb.Append(Path.GetFileName(Application.ExecutablePath));

            // Erweiterung .log anhöngen
            sb.Append(".log");

            // Name in der Klassenvariablen ablegen
            logFile = sb.ToString();

            StreamWriter sw = openLogFile();
            try
            {
                if (sw != null)
                {
                    writeInitialLog(sw);
                }
            }
            finally
            {
                sw.Close();
            }
        }




        /// <summary>
        /// Konstruktor, mit dem Pfad/Name der Protokolldatei festgelegt werden kann.
        /// </summary>
        /// <param name="logFile">Kompletter Pfadname der Protokolldatei</param>
        public Logger(string logFilePath)
        {
            // Standardwert für Protokoll-Level festlegen
            logLevel = LoggingLevel.High;

            // Dateinamen so eintragen, wie er an den Konstruktor übergeben wird
            logFile = logFilePath;

            StreamWriter sw = openLogFile();
            try
            {
                if (sw != null)
                {
                    writeInitialLog(sw);
                }
            }
            finally
            {
                sw.Close();
            }
        }
        /// <summary>
        /// Konstruktor, mit dem der Pfad und das LoggingLevel festgelegt werden kann.
        /// </summary>
        /// <param name="logFile">Pfad zur Logdatei</param>
        /// <param name="logLevel">Protokoll-Level festlegen</param>
        public Logger(string logFilePath, LoggingLevel level)
        {
            logFile = logFilePath;
            logLevel = level;

            StreamWriter sw = openLogFile();
            try
            {
                if (sw != null)
                {
                    writeInitialLog(sw);
                }
            }
            finally
            {
                sw.Close();
            }
        }

        /// <summary>
        /// Konstruktor, mit dem der Pfad und das LoggingLevel festgelegt werden kann.
        /// </summary>
        /// <param name="logFile">Pfad zur Logdatei</param>
        /// <param name="logLevel">Protokoll-Level festlegen</param>
        public Logger(string logFilePath, LoggingLevel level, bool writeDateEverytime)
        {
            logFile = logFilePath;
            logLevel = level;
            writeDateTimeEverytime = writeDateEverytime;

            StreamWriter sw = openLogFile();
            try
            {
                if (sw != null)
                {
                    writeInitialLog(sw);
                }
            }
            finally
            {
                sw.Close();
            }

        }

        /// <summary>
        /// Konstruktor, mit dem der Pfad und das LoggingLevel festgelegt werden kann.
        /// </summary>
        /// <param name="logFile">Pfad zur Logdatei</param>
        /// <param name="logLevel">Protokoll-Level festlegen</param>
        public Logger(LoggingLevel level, bool writeDateEverytime)
        {
            // Pfadname der Log-Datei wird mit einem StrinBuilder-Objekt zusammengebaut
            StringBuilder sb = new StringBuilder();

            // Pfad der ausgeführten Anwendung holen (ohne Dateinamen)
            sb.Append(Application.StartupPath);

            // Slash anhängen
            sb.Append("\\");

            // Datum im Kurzformat anhängen
            sb.Append(DateTime.Now.ToShortDateString());

            // Unterstrich anhängen
            sb.Append("_");

            // Namen der exe-Datei ermitteln und anhängen
            sb.Append(Path.GetFileName(Application.ExecutablePath));

            // Erweiterung .log anhöngen
            sb.Append(".log");

            // Name in der Klassenvariablen ablegen
            logFile = sb.ToString();

            logLevel = level;
            writeDateTimeEverytime = writeDateEverytime;

            StreamWriter sw = openLogFile();
            try
            {
                if (sw != null)
                {
                    writeInitialLog(sw);
                }
            }
            finally
            {
                sw.Close();
            }

        }


        #endregion


        #region Public Methods

        public static void LogResult(Result result)
        {
            LogResult(result,"");
        }

        public static void LogResult(Result result, string callingMethod)
        {
            // Datei öffnen lassen 
            StreamWriter sw = openLogFile();

            // Wenn das öffnen erfolgreich war:
            if (sw != null)
            {
                try
                {
                    // Datum und Uhrzeit wegschreiben
                    if (writeDateTimeEverytime)
                    {
                        writeDateTime(sw);
                    }

                    if (result.IsFailure)
                    {
                        // Informationen über die Ausnahme wegschreiben
                        writeError(sw, logLevel, result, null, callingMethod);
                    }
                    else
                        if (result.IsSuccess)
                        {
                            writeInfo(sw, logLevel, result, null, callingMethod);
                        }

                }
                finally
                {
                    // Datei wieder schließen und freigeben
                    sw.Close();

                }
            }
        }


        public static void Error(string errorMessage)
        {
            Error(errorMessage, logLevel);
        }

        public static void Error(string errorMessage, LoggingLevel level)
        {
            // Datei öffnen lassen 
            StreamWriter sw = openLogFile();

            // Wenn das öffnen erfolgreich war:
            if (sw != null)
            {
                try
                {
                    // Datum und Uhrzeit wegschreiben
                    if (writeDateTimeEverytime)
                    {
                        writeDateTime(sw);
                    }

                    StackFrame frame = new StackFrame(1);
                    Type callingType = frame.GetMethod().DeclaringType;
                    string callingMethod = frame.GetMethod().ToString();

                    // Informationen über die Ausnahme wegschreiben
                    writeError(sw, level, errorMessage, callingType, callingMethod);

                }
                finally
                {
                    // Datei wieder schließen und freigeben
                    sw.Close();

                }

            }
        }


        public static void Exception(Exception ex)
        {
            Exception(ex, logLevel);
        }


        public static void Exception(Exception ex, LoggingLevel level)
        {
            // Datei öffnen lassen 
            StreamWriter sw = openLogFile();

            // Wenn das öffnen erfolgreich war:
            if (sw != null)
            {
                try
                {
                    // Datum und Uhrzeit wegschreiben
                    if (writeDateTimeEverytime)
                    {
                        writeDateTime(sw);
                    }

                    StackFrame frame = new StackFrame(1);
                    Type callingType = frame.GetMethod().DeclaringType;
                    string callingMethod = frame.GetMethod().ToString();


                    // Informationen über die Ausnahme wegschreiben
                    writeException(sw, level, ex, callingType, callingMethod);

                }
                finally
                {
                    // Datei wieder schließen und freigeben
                    sw.Close();

                }

            }


        }

        public static void Info(string msg)
        {
            Info(msg, logLevel);
        }

        public static void Info(string msg, LoggingLevel level)
        {

            // Datei öffnen lassen 
            StreamWriter sw = openLogFile();

            // Wenn das öffnen erfolgreich war:
            if (sw != null)
            {
                try
                {
                    if (writeDateTimeEverytime)
                    {
                        // Datum und Uhrzeit wegschreiben
                        writeDateTime(sw);
                    }

                    StackFrame frame = new StackFrame(1);
                    Type callingType = frame.GetMethod().DeclaringType;
                    string callingMethod = frame.GetMethod().ToString();

                    // Informationen über die Ausnahme wegschreiben
                    writeInfo(sw, level, msg, callingType, callingMethod);

                }
                finally
                {
                    // Datei wieder schließen und freigeben
                    sw.Close();

                }

            }

        }

        #endregion


        #region Write Methods

        private void writeInitialLog(StreamWriter sw)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(DateTime.Now.ToShortDateString());
            sb.Append(" - ");
            sb.Append(DateTime.Now.ToLongTimeString());

            sw.WriteLine("########################");
            sw.WriteLine("#                       #");
            sw.WriteLine("#                       #");
            sw.WriteLine("#   Logging Started     #");
            sw.WriteLine("#                       #");
            sw.WriteLine("#         at            #");
            sw.WriteLine("#                       #");
            sw.WriteLine("# " + sb.ToString() + " #");
            sw.WriteLine("#########################");
        }


        private static void writeDateTime(StreamWriter sw)
        {
            try
            {
                sw.WriteLine("_______________________________");
                StringBuilder sb = new StringBuilder();
                sb.Append(DateTime.Now.ToShortDateString());
                sb.Append(" - ");
                sb.Append(DateTime.Now.ToLongTimeString());
                sw.WriteLine(sb.ToString());
            }
            catch
            {
                throw;
            }

        }

        private static void writeSimpleMessage(StreamWriter sw, LoggingLevel level, string msg, Type callingType, string callingMethod)
        {
            sw.WriteLine(msg);
            switch (level)
            {
                case LoggingLevel.Low:
                    break;
                case LoggingLevel.Medium:
                case LoggingLevel.High:
                    if (callingType != null)
                    {
                        sw.WriteLine("      Class: " + callingType);
                        sw.WriteLine("      Method: " + callingMethod);
                    }

                    break;
            }

        }


        private static void writeInfo(StreamWriter sw, LoggingLevel level, Result result, Type callingType, string callingMethod)
        {
            sw.WriteLine("INFO: " + callingMethod);
            sw.WriteLine(result.ToString());
            switch (level)
            {
                case LoggingLevel.Low:
                    break;
                case LoggingLevel.Medium:
                case LoggingLevel.High:
                    if (callingType != null)
                    {
                        sw.WriteLine("      Class: " + callingType);
                        sw.WriteLine("      Method: " + callingMethod);
                    }

                    break;
            }
        }

        private static void writeInfo(StreamWriter sw, LoggingLevel level, string errorMessage, Type callingType, string callingMethod)
        {
            sw.WriteLine("INFO: " + errorMessage);
            switch (level)
            {
                case LoggingLevel.Low:
                    break;
                case LoggingLevel.Medium:
                case LoggingLevel.High:
                    if (callingType != null)
                    {
                        sw.WriteLine("      Class: " + callingType);
                        sw.WriteLine("      Method: " + callingMethod);
                    }

                    break;
            }
        }



        private static void writeException(StreamWriter sw, LoggingLevel level, Exception ex, Type callingType, string callingMethod)
        {
            sw.WriteLine("########################");
            sw.WriteLine("Exception: " + ex.Message);
            switch (level)
            {
                case LoggingLevel.Low:
                    break;
                case LoggingLevel.Medium:
                    if (ex.Source != null)
                        sw.WriteLine("      Source: " + ex.Source);
                    if (ex.TargetSite != null)
                        sw.WriteLine("      TargetSite: " + ex.TargetSite.ToString());
                    if (callingType != null)
                    {
                        sw.WriteLine("      Class: " + callingType);
                        sw.WriteLine("      Method: " + callingMethod);

                    }
                    break;
                case LoggingLevel.High:
                    if (ex.Source != null)
                        sw.WriteLine("      Source: " + ex.Source);
                    if (ex.TargetSite != null)
                        sw.WriteLine("      TargetSite: " + ex.TargetSite.ToString());
                    if (callingType != null)
                    {
                        sw.WriteLine("      Class: " + callingType);
                        sw.WriteLine("      Method: " + callingMethod);
                    }
                    if (ex.StackTrace != null)
                        sw.WriteLine("      StackTrace: " + ex.StackTrace);
                    break;
            }
            sw.WriteLine("########################");
        }

        private static void writeError(StreamWriter sw, LoggingLevel level, Result result, Type callingType, string callingMethod)
        {
            sw.WriteLine("########################");
            sw.WriteLine("ERROR: " + result.Name);
            sw.WriteLine("ERROR: " + callingMethod);
            sw.WriteLine("Description: " + result.Description);
            sw.WriteLine("HRESULT: " + result.Code);
            sw.WriteLine("########################");
        }

        private static void writeError(StreamWriter sw, LoggingLevel level, string errorMessage, Type callingType, string callingMethod)
        {
            sw.WriteLine("########################");
            sw.WriteLine("ERROR: " + errorMessage);
            switch (level)
            {
                case LoggingLevel.Low:
                    break;
                case LoggingLevel.Medium:
                case LoggingLevel.High:
                    if (callingType != null)
                    {
                        sw.WriteLine("      Class: " + callingType);
                        sw.WriteLine("      Method: " + callingMethod);
                    }

                    break;
            }
            sw.WriteLine("########################");
        }



        #endregion


        #region Helper Methods

        /// <summary>
        /// Öffnet die Logdatei und fügt eine Leerzeile hinzu.
        /// Ist die Logdatei nicht vorhanden wird diese erstellt.
        /// </summary>
        /// <returns>Öffnet die Logdatei und gibt diese zurück.
        /// Sollte ein Fehler auftauchen gibt die Datei null zurück.</returns>
        private static StreamWriter openLogFile()
        {
            StreamWriter sw = null;

            try
            {
                // wenn Datei schon vorhanden ist
                if (File.Exists(logFile))
                {
                    // Datei im Append-Modus öffnen
                    sw = File.AppendText(logFile);

                    // hinter dem letzten Eintrag der Datei eine Leerzeile einfügen
                    sw.WriteLine();


                }
                // Neue Datei erstellen, wenn Sie noch nicht vorhanden ist
                else
                {
                    sw = File.CreateText(logFile);
                }


            }
            catch
            {
            }

            // Streamwrite zurückgeben, kann null sein
            return sw;


        }

        #endregion



    }
}
